import os
import shutil
import time
import cv2
import pyperclip
import threading
import tkinter as tk
from tkinter import *
from PIL import Image, ImageTk
import numpy as np
import tkinter.filedialog as tkf
from tkinterdnd2 import *
from tkinter import ttk
import tkinter.font as tkFont
import tkinter.messagebox as tkm
from subprocess import Popen, PIPE, STDOUT, TimeoutExpired
from threading import Thread, Event
from queue import Queue, Empty
from tkinter import Text, END
mask_icon = './load_file/img/mask_icon.ico'
mask_title = './load_file/img/mask_title.png'
bg_path = './load_file/bg/'
init_bg = './load_file/bg/bg_05.jpg'
run_resut_path = './run.log'
images_temp_path = './temp_images/'
weight_file = './weights/yolov3-tiny.pt'
camera_source = './photo_detection/source/'
camera_result = './photo_detection/result/'
weight_format = ['pt', 'pth']
image_format = ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'dng', 'webp', 'mpo']
video_format = ['mov', 'avi', 'mp4', 'mpg', 'mpeg', 'm4v', 'wmv', 'mkv']
class MyThread(threading.Thread):
    def __init__(self, func, *args):
        super().__init__()
        self.func = func
        self.args = args
        self.setDaemon(True)
        self.start()
    def run(self):
        self.func(*self.args)
class ProcessOutputReader(Thread):
    def __init__(self, queue, cmd, params=(),
             group=None, name=None, daemon=True):
        super().__init__(group=group, name=name, daemon=daemon)
        self._stop_request = Event()
        self.queue = queue
        self.process = Popen((cmd,) + tuple(params),
                             stdout=PIPE,
                             stderr=STDOUT,
                             universal_newlines=True)
    def run(self):
        for line in self.process.stdout:
            if self._stop_request.is_set():
                self.process.terminate()
                break
            self.queue.put(line)
            self.write_log(line)
        try:
            self.process.wait(timeout=3)
        except TimeoutExpired:
            self.process.kill()
    def stop(self):
        self._stop_request.set()
        while True:
            try:
                self.queue.get(block=False)
            except Empty:
                break
            self.queue.task_done()
    def write_log(self, line):
        if not os.path.exists(run_resut_path):
            f = open(run_resut_path, 'a+')
            f.write(line)
            f.close()
        else:
            f = open(run_resut_path, 'a+')
            f.write(line)
            f.close()
class MyConsole(Text):
    def __init__(self, parent, queue, update_interval=50, process_lines=500):
        super().__init__(parent)
        self.queue = queue
        self.update_interval = update_interval
        self.process_lines = process_lines
        self.after(self.update_interval, self.fetch_lines)
    def fetch_lines(self):
        something_inserted = False
        for _ in range(self.process_lines):
            try:
                line = self.queue.get(block=False)
            except Empty:
                break
            self.insert(END, line)
            self.queue.task_done()
            something_inserted = True
        if something_inserted:
            self.see(END)
        self.after(self.update_interval, self.fetch_lines)
top = TkinterDnD.Tk()
top.title('口罩佩戴检测')
top.geometry('1660x900+90+60')
top.resizable(0, 0)
top.config(bg='#F0F0F0')
font_Title = ("华光粗圆_CNKI", 20)
font_Radio = ("微软雅黑", 12, "bold")
font_Scale = ("微软雅黑", 9, "bold")
font_Button = ("微软雅黑", 10, "bold")
font_Label = ("微软雅黑", 10, "bold")
image_width = 640
image_height = 480
image_title_width = 108
image_title_height = 108
top.iconbitmap(mask_icon)
canvas = Canvas(top,
                bg='white',
                width=image_width,
                height=image_height)
canvas.place(x=200, y=120)
Label(top,
      text='程\n序\n输\n出',
      font=font_Radio,
      width=2,
      height=4).place(x=1600, y=305, anchor='nw')
canvas_mask_right = Canvas(top,
                           width=image_title_width,
                           height=image_title_height)
canvas_mask_right.place(x=1155, y=8)
def imgConvert(img,
               resize_flag=False,
               image_width=image_width,
               image_height=image_height):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = img.astype(np.uint8)
    img = Image.fromarray(img)
    if resize_flag:
        img = img.resize((image_width, image_height),
                         Image.ANTIALIAS)
    img = ImageTk.PhotoImage(img)
    return img
xVariable = tk.StringVar()
env_com = ttk.Combobox(top, textvariable=xVariable, width=8)
env_com.place(x=860, y=575)
def init_env_com():
    bg_list = os.listdir(bg_path)
    env_com["value"] = tuple(bg_list)
    index = bg_list.index(init_bg[-9:])
    env_com.current(index)
def xFunc(event):
    global bg
    bg_full_path = bg_path + env_com.get()
    bg_ = cv2.imread(bg_full_path)
    bg = imgConvert(bg_, True)
    canvas.create_image(0, 0,
                        anchor='nw',
                        image=bg,
                        tag='bg_img')
    top.update()
    top.after(1)
env_com.bind("<<ComboboxSelected>>", xFunc)
init_env_com()
def on_close():
    close = tkm.askokcancel("关闭", "是否关闭程序")
    if close:
        if os.path.exists('./photo_detection'):
            shutil.rmtree('./photo_detection')
        if os.path.exists(run_resut_path):
            os.remove(run_resut_path)
        top.destroy()
top.protocol("WM_DELETE_WINDOW", on_close)
def close_radio(flag):
    if flag:
        cam_01_Ra.config(state='disable')
        cam_02_Ra.config(state='disable')
        cam_03_Ra.config(state='disable')
        GPU_Ra.config(state='disable')
        CPU_Ra.config(state='disable')
        conf_thres_label_scale.config(state='disable')
        iou_thres_label_scale.config(state='disable')
        thres_Btn.config(state='disable')
        reset_canvas_Btn.config(state='disable')
        reset_Btn_state_Btn.config(state='disable')
        clear_cache_Btn.config(state='disable')
        open_dir_Btn.config(state='disable')
        conf_thres_Btn.config(state='disable')
        iou_thres_Btn.config(state='disable')
        play_cound_cbtn.config(state='disable')
        env_com.config(state='disable')
        open_camera_Btn.config(state='disable')
        select_dir_Btn.config(state='disable')
        detect_dir_Btn.config(state='disable')
        select_file_Btn.config(state='disable')
        detect_file_Btn.config(state='disable')
        weight_Btn.config(state='disable')
        link_Btn.config(state='disable')
        delete_text_Btn.config(state='disable')
        open_train_dir_Btn.config(state='disable')
        copy_run_result_Btn.config(state='disable')
        test_result_Btn.config(state='disable')
        all_result_dir_Btn.config(state='disable')
        open_code_dir_Btn.config(state='disable')
        open_dataset_dir_Btn.config(state='disable')
        iou_thres_Entry.config(state='disable')
        conf_thres_Entry.config(state='disable')
    else:
        cam_01_Ra.config(state='normal')
        cam_02_Ra.config(state='normal')
        cam_03_Ra.config(state='normal')
        GPU_Ra.config(state='normal')
        CPU_Ra.config(state='normal')
        conf_thres_label_scale.config(state='normal')
        iou_thres_label_scale.config(state='normal')
        thres_Btn.config(state='normal')
        reset_canvas_Btn.config(state='normal')
        reset_Btn_state_Btn.config(state='normal')
        clear_cache_Btn.config(state='normal')
        open_dir_Btn.config(state='normal')
        conf_thres_Btn.config(state='normal')
        iou_thres_Btn.config(state='normal')
        play_cound_cbtn.config(state='normal')
        env_com.config(state='normal')
        open_camera_Btn.config(state='normal')
        select_dir_Btn.config(state='normal')
        detect_dir_Btn.config(state='normal')
        select_file_Btn.config(state='normal')
        detect_file_Btn.config(state='normal')
        weight_Btn.config(state='normal')
        link_Btn.config(state='normal')
        delete_text_Btn.config(state='normal')
        open_train_dir_Btn.config(state='normal')
        copy_run_result_Btn.config(state='normal')
        test_result_Btn.config(state='normal')
        all_result_dir_Btn.config(state='normal')
        open_code_dir_Btn.config(state='normal')
        open_dataset_dir_Btn.config(state='normal')
        iou_thres_Entry.config(state='normal')
        conf_thres_Entry.config(state='normal')
bg = cv2.imread(init_bg)
bg = imgConvert(bg, True)
canvas.create_image(0, 0,
                    anchor='nw',
                    image=bg,
                    tag='bg_img')
img_title = cv2.imread(mask_title)
img_title = imgConvert(img_title,
                       True,
                       image_title_width,
                       image_title_height)
canvas_mask_right.create_image(0, 0,
                               anchor='nw',
                               image=img_title)
play_flag = False
def print_selection():
    global play_flag
    if play_var.get() == 1:
        play_flag = True
    else:
        play_flag = False
play_var = tk.IntVar()
play_cound_cbtn = tk.Checkbutton(top, text='播放声音', variable=play_var, onvalue=1, offvalue=0,
                                     command=print_selection)
play_cound_cbtn.place(x=72, y=80)
play_var.set(0)
conf_thres_input_value = tk.DoubleVar()
conf_thres_Entry = tk.Entry(top,
                            textvariable=conf_thres_input_value,
                            width=4)
conf_thres_Entry.place(x=315, y=815)
conf_thres_input_value.set(0.45)
conf_thres_label = tk.Label(top,
                            width=8,
                            text='Conf-thres',
                            font=font_Scale)
conf_thres_label.place(x=208, y=815)
def print_conf_thres_selection(value):
    global conf_thres
    conf_thres = value
    conf_thres_input_value.set(value)
conf_thres_label_scale = tk.Scale(top,
                                  from_=0,
                                  to=1,
                                  orient=tk.HORIZONTAL,
                                  length=250,
                                  showvalue=0,
                                  tickinterval=0.2,
                                  resolution=0.01,
                                  command=print_conf_thres_selection)
conf_thres_label_scale.place(x=207, y=840)
conf_thres_label_scale.set(0.25)
def update_conf_scale():
    conf_thres_label_scale.set(conf_thres_Entry.get())
conf_thres_Btn = tk.Button(top,
                           text="更新",
                           command=update_conf_scale,
                           relief=RIDGE,
                           font=("黑体", 9))
conf_thres_Btn.place(x=415, y=814)
iou_thres_input_value = tk.DoubleVar()
iou_thres_Entry = tk.Entry(top,
                           textvariable=iou_thres_input_value,
                           width=4)
iou_thres_Entry.place(x=582, y=815)
iou_thres_input_value.set(0.45)
iou_thres_label = tk.Label(top,
                           width=7,
                           text='Iou-thres',
                           font=font_Scale)
iou_thres_label.place(x=475, y=815)
def print_iou_thres_selection(value):
    global iou_thres
    iou_thres = value
    iou_thres_input_value.set(value)
iou_thres_label_scale = tk.Scale(top,
                                 from_=0,
                                 to=1,
                                 orient=tk.HORIZONTAL,
                                 length=250,
                                 showvalue=0,
                                 tickinterval=0.2,
                                 resolution=0.01,
                                 command=print_iou_thres_selection)
iou_thres_label_scale.place(x=475, y=840)
iou_thres_label_scale.set(0.45)
def update_iou_scale():
    iou_thres_label_scale.set(iou_thres_Entry.get())
iou_thres_Btn = tk.Button(top,
                          text="更新",
                          command=update_iou_scale,
                          relief=RIDGE,
                          font=("黑体", 9))
iou_thres_Btn.place(x=683, y=814)
def thres_reset():
    conf_thres_label_scale.set(0.25)
    iou_thres_label_scale.set(0.45)
thres_Btn = tk.Button(top,
                      text=" 阈 值 重 置 ",
                      command=thres_reset,
                      relief=RIDGE,
                      height=2,
                      font=font_Button)
thres_Btn.place(x=730, y=813)
weight_path = tk.StringVar()
def dragWeight(event):
    global weight_file
    _weight_format = ""
    for weight_format_ele in weight_format:
        _weight_format += '*.' + weight_format_ele + ' '
    _weight_format = _weight_format[:-1]
    pathList = event.data.split(' ')
    if len(pathList) > 1:
        tkm.showinfo(title='提示',
                     message='权重只能选择一个，请重新选择！')
    else:
        path = event.data
        if path.split('.')[-1] in weight_format:
            if path != '':
                path_ = path.replace('/', '\\')
                message_Isdetect = "选择的权重文件：" + path
                if tkm.askokcancel(title='开始检测',
                                   message=message_Isdetect):
                    weight_path.set("当前权重  " + path)
                    weight_file = path_
                    tkm.showinfo(title="提示",
                                 message="权重更新成功！")
                else:
                    tkm.showinfo(title="提示",
                                 message="未选择权重文件执行默认权重！")
            else:
                tkm.showinfo(title="提示",
                             message="未选择权重文件执行默认权重！")
        else:
            tkm.showinfo(title="提示",
                         message="权重文件格式须为pt或pth！")
def selectWeight():
    global weight_file
    _weight_format = ""
    for weight_format_ele in weight_format:
        _weight_format += '*.' + weight_format_ele + ' '
    _weight_format = _weight_format[:-1]
    path = tkf.askopenfilename(filetypes=
                               [("weight file", _weight_format)])
    if path != '':
        path_ = path.replace('/', '\\')
        message_Isdetect = "选择的权重文件：" + path
        if tkm.askokcancel(title='开始检测',
                           message=message_Isdetect):
            weight_path.set("当前权重  " + path)
            weight_file = path_
            tkm.showinfo(title="提示",
                         message="权重更新成功！")
        else:
            tkm.showinfo(title="提示",
                         message="未选择权重文件执行默认权重！")
    else:
        tkm.showinfo(title="提示",
                     message="未选择权重文件执行默认权重！")
weight_Entry = tk.Entry(top,
                        textvariable=weight_path,
                        width=57)
weight_Entry.place(x=210, y=720)
weight_Entry.drop_target_register(DND_FILES)
weight_Entry.dnd_bind('<<Drop>>', dragWeight)
weight_path.set("当前权重 ( "+ weight_file +" [可拖拽文件到此处] )")
weight_Btn = tk.Button(top,
                       text=" 选 择 权 重 ",
                       command=selectWeight,
                       relief=RIDGE,
                       font=font_Button)
weight_Btn.place(x=730, y=710)
var = tk.IntVar()
device_Label = tk.Label(top,
              width=13,
              height=1,
              text='empty',
              font=font_Button)
device_Label.place(x=860, y=115)
device_parameter = {'GPU': '0', 'CPU': 'cpu'}
device_dict = {0: "GPU", 1: "CPU"}
var.set(0)
device_Label.config(text='计算设备 [ ' + device_dict[var.get()] + ' ]')
device_parameter_elem = device_parameter[device_dict[var.get()]]
def device_trigger():
    global device_parameter_elem
    device_Label.config(text='计算设备 [ ' + device_dict[var.get()] + ' ]')
    device_parameter_elem = device_parameter[device_dict[var.get()]]
GPU_Ra = tk.Radiobutton(top,
                        text='GPU',
                        variable=var,
                        value=0,
                        command=device_trigger,
                        font=font_Button)
GPU_Ra.place(x=1010, y=113)
CPU_Ra = tk.Radiobutton(top,
                        text='CPU',
                        variable=var,
                        value=1,
                        command=device_trigger,
                        font=font_Button)
CPU_Ra.place(x=1100, y=113)
var_camdev = tk.IntVar()
l2 = tk.Label(top,
              width=9,
              height=1,
              text='empty',
              font=font_Button)
l2.place(x=1260, y=115)
camera_parameter = {'#1': 0, '#2': 1, '#3': 2}
camera_dict = {0: "#1", 1: "#2", 2: "#3"}
var_camdev.set(0)
l2.config(text='摄像头 [ ' + camera_dict[var_camdev.get()] + ' ]')
camera_parameter_elem = camera_parameter[camera_dict[var_camdev.get()]]
def camera_trigger():
    global camera_parameter_elem
    l2.config(text='摄像头 [ ' + camera_dict[var_camdev.get()] + ' ]')
    camera_parameter_elem = camera_parameter[camera_dict[var_camdev.get()]]
cam_01_Ra = tk.Radiobutton(top,
                           text='#1',
                           variable=var_camdev,
                           value=0,
                           command=camera_trigger,
                           font=font_Radio)
cam_01_Ra.place(x=1370, y=113)
cam_02_Ra = tk.Radiobutton(top,
                           text='#2',
                           variable=var_camdev,
                           value=1,
                           command=camera_trigger,
                           font=font_Radio)
cam_02_Ra.place(x=1445, y=113)
cam_03_Ra = tk.Radiobutton(top,
                           text='#3',
                           variable=var_camdev,
                           value=2,
                           command=camera_trigger,
                           font=font_Radio)
cam_03_Ra.place(x=1520, y=113)
def get_params(useCamera=True) ->list :
    params = ['-u', 'detect.py']
    if device_parameter_elem == '0':
        params.append('-d')
        params.append(device_parameter_elem)
    elif device_parameter_elem == 'cpu':
        params.append('-d')
        params.append(device_parameter_elem)
    if play_flag == True:
        params.append('-ps')
        params.append('open')
    else:
        params.append('-ps')
        params.append('close')
    params.append('-w')
    params.append(weight_file)
    params.append('--conf-thres')
    params.append(conf_thres)
    params.append('--iou-thres')
    params.append(iou_thres)
    if useCamera:
        params.append('-s')
        params.append('0')
    return params
def open_camera():
    global cap, frame_
    open_camera_Btn.config(state='disable')
    close_camrea_Btn.config(state='normal')
    photo_detection_Btn.config(state='normal')
    close_radio(True)
    try:
        cap = cv2.VideoCapture(camera_parameter_elem)
    except Exception:
        tkm.showinfo(title="提示",
                     message="该相机设备不存在, 自动跳转存在设备！")
    while True:
        ref, frame = cap.read()
        frame_ = cv2.flip(frame, 1)
        tkimg = imgConvert(frame)
        canvas.create_image(0, 0,
                            anchor='nw',
                            image=tkimg)
        temp = tkimg
        top.update()
        top.after(1)
open_camera_Btn = tk.Button(top,
                            text='打开摄像头',
                            command=lambda: MyThread(open_camera),
                            relief=RIDGE,
                            width=9,
                            height=2,
                            font=font_Button,
                            )
open_camera_Btn.place(x=60, y=120)
def close_camrea():
    try:
        open_camera_Btn.config(state='normal')
        close_camrea_Btn.config(state='disable')
        photo_detection_Btn.config(state='disable')
        cap.release()
        close_radio(False)
        canvas.create_image(0, 0,
                            anchor='nw',
                            image=bg)
        tkm.showinfo(title="提示",
                     message="摄像头已关闭！")
    except NameError:
        pass
    finally:
        open_camera_Btn.config(state='normal')
        close_camrea_Btn.config(state='disable')
        photo_detection_Btn.config(state='disable')
        close_radio(False)
close_camrea_Btn = tk.Button(top,
                             text='关闭摄像头',
                             command=lambda: MyThread(close_camrea),
                             relief=RIDGE, state='disable',
                             width=9,
                             height=2,
                             font=font_Button)
close_camrea_Btn.place(x=60, y=280)
def photo_detection():
    open_camera_Btn.config(state='normal')
    close_camrea_Btn.config(state='disable')
    photo_detection_Btn.config(state='disable')
    if not os.path.exists(camera_source):
        os.makedirs(camera_source)
    infile_path = camera_source + 'detect.jpg'
    photo_detection_Btn.config(state='disable')
    global frame_
    cv2.imwrite(infile_path, frame_)
    tkm.showinfo(title="提示",
                 message="照片已保存至" + infile_path)
    canvas.delete('bg_img')
    cap.release()
    close_radio(False)
    detect_camera = cv2.imread(infile_path)
    detect_camera = imgConvert(detect_camera, True)
    params = get_params(useCamera=False)
    params.append('-s')
    params.append(infile_path)
    params.append('--project')
    params.append('photo_detection')
    params.append('--name')
    params.append('result')
    reader = ProcessOutputReader(line_queue, 'python', params=params)
    reader.start()
    global num_runs
    num_runs = -1
    canvas.delete('bg_img')
    tkm.showinfo(title="提示",
                 message="照片已在检测中，注意查看右侧输出及左侧结果！")
    canvas.create_image(0, 0,
                        anchor='nw',
                        image=detect_camera,
                        tag='s_img')
    temp = detect_camera
    top.update()
    top.after(1)
    if time.sleep(6) == None:
        outfile_path = camera_result + 'detect.jpg'
        result_camera = cv2.imread(outfile_path)
        result_camera = imgConvert(result_camera, True)
        canvas.delete('s_img')
        canvas.create_image(0, 0,
                            anchor='nw',
                            image=result_camera,
                            tag='r_img')
        temp = result_camera
        top.update()
        top.after(1)
        if time.sleep(10) == None:
            canvas.delete('r_img')
            canvas.create_image(0, 0,
                                anchor='nw',
                                image=bg)
            temp = bg
photo_detection_Btn = tk.Button(top,
                                text='拍照检测',
                                command=lambda: MyThread(photo_detection),
                                relief=RIDGE, state='disable',
                                width=9,
                                height=2,
                                font=font_Button)
photo_detection_Btn.place(x=60, y=200)
num_runs = 0
def start_detect():
    try:
        if os.path.exists('./temp_images'):
            shutil.rmtree('./temp_images')
        if not os.path.exists('./temp_images/'):
            os.mkdir('./temp_images/')
    except FileNotFoundError:
        pass
    global reader
    global num_runs
    try:
        start_detect_Btn.config(state='disable')
        stop_detect_Btn.config(state='normal')
        params = get_params(useCamera=False)
        params.append('-s')
        params.append(str(camera_parameter_elem))
        reader = ProcessOutputReader(line_queue, 'python', params=params)
        reader.start()
        close_radio(True)
        num_runs = 1
        max = -1
        while True:
            max_ = max
            if os.listdir(images_temp_path):
                files = os.listdir(images_temp_path)
                for file in files:
                    file_num = int(file.split('.')[0])
                    if file_num > max:
                        max = int(file_num)
            if max_ > 0 and max_ < max:
                filename = images_temp_path + str(max_).zfill(8) + '.jpg'
                dynamic_images = cv2.imread(filename)
                dynamic_images = imgConvert(dynamic_images)
                canvas.create_image(0, 0, anchor='nw', image=dynamic_images)
                temp = dynamic_images
                top.update()
                top.after(1)
    except EOFError:
        tkm.showerror(title="错误",
                      message="权重文件加载失败，请重新选择！")
    except FileNotFoundError:
        pass;
start_detect_Btn = tk.Button(top,
                             text='开始检测',
                             command=lambda: MyThread(start_detect),
                             relief=RIDGE,
                             width=9,
                             height=2,
                             font=font_Button)
start_detect_Btn.place(x=60, y=360)
def stop_detect():
    try:
        start_detect_Btn.config(state='normal')
        stop_detect_Btn.config(state='disable')
        reader.stop()
        reader.join(timeout=5)
        canvas.create_image(0, 0, anchor='nw', image=bg)
        close_radio(False)
        tkm.showinfo(title="提示",
                     message="已停止检测！")
        if os.listdir(images_temp_path):
            file_list = os.listdir(images_temp_path)
            for file in file_list:
                image_temp_path = images_temp_path + file
                os.remove(image_temp_path)
        if reader.is_alive():
            raise RuntimeError("process output reader failed to stop")
    except RuntimeError:
        tkm.showinfo(title="提示",
                     message="检测已运行，请勿重复点击！")
    except (UnicodeDecodeError, NameError):
        pass
    finally:
        start_detect_Btn.config(state='normal')
        stop_detect_Btn.config(state='disable')
        close_radio(False)
stop_detect_Btn = tk.Button(top,
                            text='停止检测',
                            command=lambda: MyThread(stop_detect),
                            relief=RIDGE,
                            state='disable',
                            width=9,
                            height=2,
                            font=font_Button)
stop_detect_Btn.place(x=60, y=440)
def current_result():
    result_dir_name = './runs/detect/'
    current_path = os.getcwd()
    result_dir_name = current_path + result_dir_name
    try:
        if os.listdir(result_dir_name):
            files = os.listdir(result_dir_name)
            index = [1]
            for dir_file in files:
                dir_file = dir_file[3:]
                if dir_file != '':
                    dir_file = int(dir_file)
                    index.append(dir_file)
        sort_index = sorted(index)
        max_index = sort_index[-1]
        if max_index and max_index != 1:
            if num_runs == 0:
                tkm.showinfo(title="提示",
                             message="未运行程序，无检测结果！")
            elif num_runs == -1:
                current_path = os.getcwd()
                camera_result_path = current_path + '/photo_detection/result/'
                os.startfile(camera_result_path)
            else:
                for num in range(num_runs):
                    path = result_dir_name + 'exp' + str(max_index)
                    os.startfile(path)
                    del sort_index[-1]
                    max_index = sort_index[-1]
        elif max_index == 1:
            path = result_dir_name + 'exp'
            os.startfile(path)
            time.sleep(0.25)
    except UnboundLocalError:
        tkm.showinfo(title="提示",
                     message="检测结果为空！")
open_dir_Btn = tk.Button(top,
                         text="检测结果",
                         command=lambda: MyThread(current_result),
                         relief=RIDGE,
                         width=9,
                         height=2,
                         font=font_Button)
open_dir_Btn.place(x=60, y=520)
def clear_cache():
    if tkm.askokcancel(title='清空缓存',
                       message="是否清空程序缓存\n"
                               "(包括运行日志和图片缓存，当运行结果复制不正确需要此功能)"):
        file_list = os.listdir(images_temp_path)
        for file in file_list:
            image_temp_path = images_temp_path + file
            os.remove(image_temp_path)
        if os.path.exists('run.log'):
            os.remove("run.log")
        if os.path.exists('./photo_detection/'):
            shutil.rmtree('./photo_detection/')
        if not os.listdir(images_temp_path) and not os.path.exists("run.log") \
                and not os.path.exists('./photo_detection/'):
            tkm.showinfo(title="提示",
                         message="缓存已清空！")
clear_cache_Btn = tk.Button(top,
                            text="清空缓存",
                            command=lambda: MyThread(clear_cache),
                            relief=RIDGE,
                            width=9,
                            height=2,
                            font=font_Button)
clear_cache_Btn.place(x=60, y=600)
def reset_canvas():
    if tkm.askokcancel(title='重置画布',
                       message="是否将画布重置到初始状态(当画布显示不正确需要此功能)"):
        canvas.delete('All')
        canvas.create_image(0, 0,
                            anchor='nw',
                            image=bg,
                            tag='bg_img')
        tkm.showinfo(title="提示",
                     message="画布已重置！")
reset_canvas_Btn = tk.Button(top,
                             text="重置画布",
                             command=lambda: MyThread(reset_canvas),
                             relief=RIDGE,
                             width=9,
                             height=2,
                             font=font_Button)
reset_canvas_Btn.place(x=60, y=680)
def reset_Btn_state():
    if tkm.askokcancel(title='重置按钮状态',
                       message="是否重置按钮到初始状态(非必要请勿点击)"):
        close_radio(False)
        open_camera_Btn.config(state='normal')
        photo_detection_Btn.config(state='disable')
        close_camrea_Btn.config(state='disable')
        start_detect_Btn.config(state='normal')
        stop_detect_Btn.config(state='disable')
        tkm.showinfo(title="提示",
                     message="按钮状态已重置！")
reset_Btn_state_Btn = tk.Button(top,
                                text="重置按钮状态",
                                command=lambda: MyThread(reset_Btn_state),
                                relief=RIDGE,
                                width=9,
                                height=2,
                                font=font_Button)
reset_Btn_state_Btn.place(x=60, y=760)
line_queue = Queue(maxsize=2000)
console = MyConsole(top, line_queue)
console.place(x=860, y=150)
dir_path = tk.StringVar()
def dragDir(event):
    global selecct_dir_path
    selecct_dir_path = event.data
    mulFile_flag = False
    sinFile_flag = False
    flag = False
    count_image = 0
    count_video = 0
    global num_runs
    if len(event.data.split(' ')) > 1:
        mulFile_flag = True
    if event.data.split(' ')[0].find('.') != -1:
        sinFile_flag = True
    if mulFile_flag == False and sinFile_flag == False:
        dir_path.set(event.data)
        try:
            if os.listdir(selecct_dir_path):
                files = os.listdir(selecct_dir_path)
                for file in files:
                    fulldirct = os.path.join(selecct_dir_path, file)
                    if os.path.isfile(fulldirct):
                        suffix = file.split('.')[1]
                        if suffix in image_format:
                            count_image += 1
                        elif suffix in video_format:
                            count_video += 1
                        else:
                            flag = True
            count = count_video + count_image
            if count == 0:
                num_runs = 0
                tkm.showinfo(title='提示',
                             message='该文件夹内无图像或视频文件，请重新选择！')
            elif flag == True:
                num_runs = 0
                tkm.showinfo(title='提示',
                             message='该文件夹有不支持的文件格式')
            else:
                dir_path.set(selecct_dir_path)
                global message_Isdetectdir
                message_Isdetectdir = "共检测到" + str(count_image) + \
                                      "张图片和" + str(count_video) + "个视频文件，确认开始检测？"
                tkm.showinfo(title="提示",
                             message=message_Isdetectdir[:-8])
        except FileNotFoundError:
            num_runs = 0
            dir_path.set("文件夹检测( 将要检测的图片或视频放到检测的文件夹下 )")
            tkm.showinfo(title="提示",
                         message="未选择文件！")
    elif mulFile_flag == True:
        tkm.showinfo(title="提示",
                     message="多文件检查请拖入下行！")
    elif sinFile_flag == True:
            tkm.showinfo(title="提示",
                         message="单文件检查请拖入下行！")
def selectDir():
    global selecct_dir_path
    selecct_dir_path = tkf.askdirectory()
    flag = False
    count_image = 0
    count_video = 0
    global num_runs
    try:
        if os.listdir(selecct_dir_path):
            files = os.listdir(selecct_dir_path)
            for file in files:
                fulldirct = os.path.join(selecct_dir_path, file)
                if os.path.isfile(fulldirct):
                    suffix = file.split('.')[1]
                    if suffix in image_format:
                        count_image += 1
                    elif suffix in video_format:
                        count_video += 1
                    else:
                        flag = True
        count = count_video + count_image
        if count == 0:
            num_runs = 0
            tkm.showinfo(title='提示',
                         message='该文件夹内无图像或视频文件，请重新选择！')
        elif flag == True:
            num_runs = 0
            tkm.showinfo(title='提示',
                         message='该文件夹有不支持的文件格式')
        else:
            dir_path.set(selecct_dir_path)
            global message_Isdetectdir
            message_Isdetectdir = "共检测到" + str(count_image) + \
                                  "张图片和" + str(count_video) + "个视频文件，确认开始检测？"
            tkm.showinfo(title="提示",
                         message=message_Isdetectdir[:-8])
    except FileNotFoundError:
        num_runs = 0
        dir_path.set("文件夹检测( 将要检测的图片或视频放到检测的文件夹下 )")
        tkm.showinfo(title="提示",
                     message="未选择文件！")
def detectDir():
    global num_runs
    try:
        if tkm.askokcancel(title='开始检测',
                           message=message_Isdetectdir):
            num_runs = 1
            tkm.showinfo(title="提示",
                         message="检测中，关闭该窗口查看右侧文本输出窗口查看程序输出结果！")
            params = get_params(useCamera=False)
            params.append('-s')
            params.append(selecct_dir_path)
            reader = ProcessOutputReader(line_queue, 'python', params=params)
            reader.start()
    except NameError:
        tkm.showinfo(title="提示",
                     message="请先选择文件夹！")
dir_Entry = tk.Entry(top,
                     textvariable=dir_path,
                     width=57)
dir_Entry.place(x=210, y=620)
dir_path.set("文件夹检测( 选择包含图片和视频的文件夹[可拖拽文件夹到此处] )")
dir_Entry.drop_target_register(DND_FILES)
dir_Entry.dnd_bind('<<Drop>>', dragDir)
select_dir_Btn = tk.Button(top,
                           text="选择",
                           command=selectDir,
                           relief=RIDGE,
                           font=font_Button)
select_dir_Btn.place(x=730, y=610)
detect_dir_Btn = tk.Button(top,
                           text="检测",
                           command=detectDir,
                           relief=RIDGE,
                           font=font_Button)
detect_dir_Btn.place(x=790, y=610)
file_path = tk.StringVar()
def dragFile(event):
    global selecct_file_path
    selecct_file_path = tuple(event.data.split(' '))
    path_list = event.data.split(' ')
    img_vid_format = image_format + video_format
    exist_flag = False
    for file in path_list:
        if file.split('.')[-1] not in img_vid_format:
            exist_flag = True
    dir_flag = False
    if event.data.find('.') == -1:
        dir_flag = True
    if exist_flag == False and dir_flag == False:
        file_path.set(event.data)
        global num_runs
        count_image = 0
        count_video = 0
        for file in selecct_file_path:
            file_format = file.split('.')[1]
            if file_format in image_format:
                count_image += 1
            elif file_format in video_format:
                count_video += 1
        count = count_image + count_video
        if count != 0:
            file_path.set(selecct_file_path)
            global path_
            path_ = ''
            for index in range(len(selecct_file_path)):
                path_ += selecct_file_path[index] + ' '
            path_ = path_[:-1].replace('/', '\\')
            path_ = path_.split(' ')
            global message_IsdetectFile
            message_IsdetectFile = "共检测到" + str(count_image) + \
                                   "张图片和" + str(count_video) + \
                                   "个视频文件，确认开始检测？"
            tkm.showinfo(title="提示",
                         message=message_IsdetectFile[:-8])
    elif dir_flag == True:
        tkm.showinfo(title="提示",
                     message='文件夹请拖入上行！')
    elif exist_flag == True:
        tkm.showinfo(title="提示",
                     message='文件格式非图片或视频，请重新选择！')
def selectFile():
    global num_runs
    _image_format = ""
    _video_format = ""
    for image_format_ele in image_format:
        _image_format += '*.' + image_format_ele + ' '
    _image_format = _image_format[:-1]
    for video_format_ele in video_format:
        _video_format += '*.' + video_format_ele + ' '
    _video_format = _video_format[:-1]
    path = tkf.askopenfilenames(
        filetypes=[("image file", _image_format),
                   ("video file", _video_format)])
    count_image = 0
    count_video = 0
    for file in path:
        file_format = file.split('.')[1]
        if file_format in image_format:
            count_image += 1
        elif file_format in video_format:
            count_video += 1
    count = count_image + count_video
    if count != 0:
        file_path.set(path)
        global path_
        path_ = ''
        for index in range(len(path)):
            path_ += path[index] + ' '
        path_ = path_[:-1].replace('/', '\\')
        path_ = path_.split(' ')
        global message_IsdetectFile
        message_IsdetectFile = "共检测到" + str(count_image) + \
                               "张图片和" + str(count_video) + \
                               "个视频文件，确认开始检测？"
        tkm.showinfo(title="提示",
                     message=message_IsdetectFile[:-8])
def detectFile():
    global num_runs
    try:
        if tkm.askokcancel(title='开始检测',
                           message=message_IsdetectFile):
            params = get_params(useCamera=False)
            params.append('-s')
            for path_ele in path_:
                params.append(path_ele)
            num_runs = len(path_)
            tkm.showinfo(title="提示",
                         message="检测中，关闭该窗口查看右侧文本输出窗口查看程序输出结果！")
            reader = ProcessOutputReader(line_queue, 'python', params=params)
            reader.start()
        else:
            num_runs = 0
            tkm.showinfo(title="提示",
                         message="用户取消检测！")
    except NameError:
        tkm.showinfo(title="提示",
                     message="请先选择文件！")
file_Entry = tk.Entry(top,
                      textvariable=file_path,
                      width=57)
file_Entry.place(x=210, y=670)
file_Entry.drop_target_register(DND_FILES)
file_Entry.dnd_bind('<<Drop>>', dragFile)
file_path.set("文件检测(  选择图片和视频[可多选、可拖拽文件到此处]  )")
select_file_Btn = tk.Button(top,
                            text="选择",
                            command=selectFile,
                            relief=RIDGE,
                            font=font_Button)
select_file_Btn.place(x=730, y=660)
detect_file_Btn = tk.Button(top,
                            text="检测",
                            command=detectFile,
                            relief=RIDGE,
                            font=font_Button)
detect_file_Btn.place(x=790, y=660)
link_path = tk.StringVar()
def selectPath():
    global num_runs
    global reader
    message_Islink_path = "确认开始检测链接：" + link_path.get()
    if tkm.askokcancel(title='开始检测',
                       message=message_Islink_path):
        print(link_path.get())
        if link_path.get().startswith("https://") or \
                link_path.get().startswith("http://"):
            num_runs = 1
            params = get_params(useCamera=False)
            params.append('-s')
            params.append(link_path.get())
            try:
                close_radio(True)
                start_detect_Btn.config(state='disable')
                stop_detect_Btn.config(state='normal')
                reader = ProcessOutputReader(line_queue, 'python', params=params)
                reader.start()
                close_radio(False)
            except AssertionError:
                num_runs = 0
                tkm.showinfo(title='提示',
                             message="该链接不是视频链接，请重新输入！")
            close_radio(True)
            start_detect_Btn.config(state='disable')
            stop_detect_Btn.config(state='normal')
        else:
            num_runs = 0
            tkm.showinfo(title='提示',
                         message="链接格式不正确，请重新输入！")
    else:
        num_runs = 0
        tkm.showinfo(title="提示",
                     message="用户取消检测！")
link_Entry = tk.Entry(top,
                      textvariable=link_path,
                      width=57)
link_Entry.place(x=210, y=770)
link_path.set("链接检测( 放入单个视频网页链接 )")
link_Btn = tk.Button(top,
                     text=" 链 接 检 测 ",
                     command=selectPath,
                     relief=RIDGE,
                     font=font_Button)
link_Btn.place(x=730, y=760)
def delete_text():
    console.delete('1.0', END)
    tkm.showinfo(title='提示',
                 message="清空成功！")
delete_text_Btn = tk.Button(top,
                            text='清空文本',
                            command=delete_text,
                            relief=RIDGE,
                            width=9,
                            height=2,
                            font=font_Button)
delete_text_Btn.place(x=1050, y=580)
def copy_run_result():
    if not os.path.exists(run_resut_path):
        tkm.showinfo(title='提示',
                     message="当前还未产生结果，请运行程序后再复制！")
        return
    else:
        f = open(run_resut_path, 'r')
        result_lines = f.readlines()
        if result_lines == []:
            if tkm.askokcancel(title='复制结果',
                               message="当前运行结果为空，是否继续复制！"):
                pyperclip.copy('说了空的你还不信！v(￣_￣|||)')
                spam = pyperclip.paste()
                tkm.showinfo(title='提示',
                             message="复制成功！")
            else:
                tkm.showinfo(title='提示',
                             message="用户取消！")
        else:
            temp_run_result = ''
            for result_line in result_lines:
                temp_run_result += result_line
            pyperclip.copy(temp_run_result)
            spam = pyperclip.paste()
            tkm.showinfo(title='提示',
                         message="复制成功！")
copy_run_result_Btn = tk.Button(top,
                                text='复制结果',
                                command=copy_run_result,
                                relief=RIDGE,
                                width=9,
                                height=2,
                                font=font_Button)
copy_run_result_Btn.place(x=1300, y=580)
def train_dir():
    current_path = os.getcwd()
    absolute_path = current_path + '/runs/train/'
    try:
        os.startfile(absolute_path)
    except FileNotFoundError:
        tkm.showinfo(title='提示',
                     message="当前目录为空！")
open_train_dir_Btn = tk.Button(top,
                               text='训练结果',
                               command=train_dir,
                               relief=RIDGE,
                               width=9,
                               height=2,
                               font=font_Button)
open_train_dir_Btn.place(x=900, y=670)
def test_result():
    current_path = os.getcwd()
    absolute_path = current_path + '/runs/test/'
    try:
        os.startfile(absolute_path)
    except FileNotFoundError:
        tkm.showinfo(title='提示',
                     message="当前目录为空！")
test_result_Btn = tk.Button(top,
                                text='测试结果',
                                command=test_result,
                                relief=RIDGE,
                                width=9,
                                height=2,
                                font=font_Button)
test_result_Btn.place(x=1177, y=670)
def detect_result():
    current_path = os.getcwd()
    absolute_path = current_path + '/runs/detect/'
    try:
        os.startfile(absolute_path)
    except FileNotFoundError:
        tkm.showinfo(title='提示',
                     message="当前目录为空！")
all_result_dir_Btn = tk.Button(top,
                               text='检测结果',
                               command=lambda: MyThread(detect_result),
                               relief=RIDGE,
                               width=9,
                               height=2,
                               font=font_Button)
all_result_dir_Btn.place(x=1450, y=670)
def open_code_dir():
    current_path = os.getcwd()
    os.startfile(current_path)
open_code_dir_Btn = tk.Button(top,
                              text='代码',
                              command=lambda: MyThread(open_code_dir),
                              relief=RIDGE,
                              width=9,
                              height=2,
                              font=font_Button)
open_code_dir_Btn.place(x=1050, y=760)
def open_dataset_dir():
    current_path = os.getcwd()
    dataset_image_path = '/data/images/'
    dataset_label_path = '/data/Annotations/'
    dataset_image_path = current_path + dataset_image_path
    dataset_label_path = current_path + dataset_label_path
    try:
        os.startfile(dataset_image_path)
        time.sleep(0.25)
        os.startfile(dataset_label_path)
    except FileNotFoundError:
        tkm.showinfo(title='提示',
                     message="文件夹为空！")
open_dataset_dir_Btn = tk.Button(top,
                                 text='数据集',
                                 command=lambda: MyThread(open_dataset_dir),
                                 relief=RIDGE,
                                 width=9,
                                 height=2,
                                 font=font_Button)
open_dataset_dir_Btn.place(x=1300, y=760)
def local_detect():
    global num_runs
    global reader
    num_runs = 1
    params = get_params(useCamera=True)
    params.append('-dr')
    params.append('open')
    try:
        close_radio(True)
        start_detect_Btn.config(state='disable')
        stop_detect_Btn.config(state='normal')
        reader = ProcessOutputReader(line_queue, 'python', params=params)
        reader.start()
        close_radio(False)
    except AssertionError:
        pass
    close_radio(True)
    start_detect_Btn.config(state='disable')
    stop_detect_Btn.config(state='normal')
local_detect_Btn = tk.Button(top,
                             text='基于yolov3-tiny的口罩佩戴检测程序',
                             command=lambda: MyThread(local_detect),
                             relief=RIDGE,
                             width=32,
                             height=1,
                             bd=0,
                             font=font_Title,
                             )
local_detect_Btn.place(x=570, y=35, anchor='nw')
cv2.destroyAllWindows()
top.mainloop()